/*
 * @FileName: mppt.c
 * @Author: GreyQiu qiushaogui@aikosolar.com
 * @Date: 2023-07-21 11:51:16
 * @LastEditors: GreyQiu qiushaogui@aikosolar.com
 * @LastEditTime: 2023-09-18 09:02:29
 * @Description: 
 * 
 * Copyright (c) 2023, All Rights Reserved. 
 */

#include "mppt.h"
#include "log.h"
#include "loop.h"

/**
 * @description: MPPT追踪算法-扰动观察法
 * @param mppt MPPT对象
 * @return 无
 */
static bool mppt_track(mppt_t * mppt, float pwr)
{
    bool rtn = false;
    if (mppt->find_mpp) { // 找到最大功率点
        mppt->find_mpp = false;
        // return true;
    }

//    float iin = *(mppt->iin_addr);
//    float vin = *(mppt->vin_addr);
//    float pwr = vin * iin;              // 计算当前功率
    float deltaP = pwr - mppt->pwr_pre; // 计算上一步扰动的deltaP
    int8_t dir = mppt->dir[0];          // 获取前一次的扰动方向

    if (deltaP < 0) { // 观察上一步扰动结果，确定扰动方向
        dir = -dir;
    }

    // 输出超出范围，必须往回扰动
    if (mppt->out >= mppt->outMax) {
        dir = -1;
    } else if (mppt->out <= mppt->outMin) {
        dir = 1;
    }

    // 施加扰动
    mppt->out = mppt->out + mppt->step * dir;
    (mppt->loop->ref) = mppt->out;

    // 两步一回头，说明找到最大功率点
    if (mppt->dir[1] == mppt->dir[0] &&
        dir == -mppt->dir[0]) {
        mppt->find_mpp = true;
        rtn = true;
    }

    if (mppt->find_mpp) { // 找到最大功率点，逐步减小扰动步长
        mppt->cnt_step = 8;
        // 功率变化足够大才允许减小步长
        mppt->step = mppt->step - mppt->stepGrad;
        if (mppt->step < mppt->stepMin) {
            mppt->step = mppt->stepMin;
        }
    } else { // 没有找到最大功率点，逐步加大扰动步长
        if (mppt->cnt_step > 0) {
            mppt->cnt_step--;
        } else {
            // 功率变化不够大且步长没有展开到最大，都允许加大步长
            mppt->step = mppt->step + mppt->stepGrad;
            if (mppt->step > mppt->stepMax) {
                mppt->step = mppt->stepMax;
            }
        }
    }

    // 更新历史信息，供下次扰动方向的判断
    mppt->pwr_pre = pwr;
    mppt->dir[1] = mppt->dir[0];
    mppt->dir[0] = dir;

    return rtn;
}

/**
 * @description: mppt复位
 * @param mppt
 * @return 
 */
static void mppt_reset(mppt_t * mppt)
{
    float iin = *(mppt->iin_addr);
    float vin = *(mppt->vin_addr);
    float pwr = vin * iin;              // 计算当前功率
    float deltaP = pwr - mppt->pwr_pre; // 计算上一步扰动的deltaP
    int8_t dir = 1; // 饱和说明基准太低 需要加大基准退饱和

    mppt->step = mppt->stepMax;
    // 施加扰动
    mppt->out = vin + mppt->stepMax * dir;

    if (mppt->out > mppt->outMax) {
        mppt->out = mppt->outMax;
    }
    mppt->loop->ref = mppt->out;
    // 更新历史信息，供下次扰动方向的判断
    mppt->pwr_pre = pwr;
    mppt->dir[1] = 0;
    mppt->dir[0] = dir;
}

float vinsum = 0;
float iinsum = 0;

/**
 * @description: MPPT处理函数
 * @param mppt MPPT对象
 * @return 无
 */
void mppt_handle(mppt_t * mppt)
{
    #define DELAY_MPP       1       // MPP点停留周期计数
    #define DELAY_BYPASS    50      // 直通停留周期计数

    vinsum += *(mppt->vin_addr);
    iinsum += *(mppt->iin_addr);
//    }
    // 运行周期分频
    if (++(mppt->cnt_prescale) < mppt->div) {
        return;
    }
    float iin = iinsum / (mppt->cnt_prescale);
    float vin = vinsum / (mppt->cnt_prescale);
    iinsum = 0;
    vinsum = 0;
    mppt->cnt_prescale = 0;

    float pwr = vin * iin;              // 计算当前功率

    // 更新VOC
    if (loop_get_duty() < 0.001f && pwr < 6.0f) { // 占空比很小且输出功率很小
        mppt->voc = vin;
//        mppt->out = vin * 0.9f;
//        mppt->loop->ref = mppt->out;
        mppt->outMax = vin + MPPT_VOC_OFFSET;
    } else if (vin > mppt->voc) { // 电池板电压增大
        mppt->voc = vin;
        mppt->outMax = vin + MPPT_VOC_OFFSET;
    }

    switch (mppt->state)
    {
        case MPPT_STATE_DISABLE:
            break;

        case MPPT_STATE_TRACK:
            if (mppt_track(mppt, pwr) == true) { // 找到mppt点,会多停留一个MPPT周期
                mppt->state = MPPT_STATE_DELAY; // 在MPP点处停留，以提高MPPT效率
                mppt->cnt_delay = 0;
            }
            break;

        case MPPT_STATE_DELAY:
//            if (loop_get_duty() > GLOOP_OUT_MAX_MPPT - 0.01f) { // 如果MPPT环路饱和 切换到直通态
//                mppt->loop->Ymax = GLOOP_OUT_MAX_BYPASS; // 允许直通，放开最大占空比
//                mppt->loop->ref =  mppt->out - mppt->stepMax * 2; // 基准设到最低，占空比能够达到100%
//                mppt->state = MPPT_STATE_BYPASS;
//                mppt->cnt_delay = 0;
//            } else 
            if (++mppt->cnt_delay > DELAY_MPP) { // MPP停留周期数为DELAY_MPP + 2
                mppt->cnt_delay = 0;
                mppt->state = MPPT_STATE_TRACK; // 返回MPPT追踪态
            } 
            break;

        case MPPT_STATE_BYPASS:
            if (bsp_gpio_read(PORT_TEST, PIN_TEST) == 0 && (++mppt->cnt_delay > DELAY_BYPASS)) { // 直通态维持一定时间，返回MPPT追踪态尝试查找更大功率点
                mppt->cnt_delay = 0;
                mppt->loop->Ymax = GLOOP_OUT_MAX_MPPT; // 限制最大占空比
                mppt->state = MPPT_STATE_TRACK;
                mppt_reset(mppt);
            }
            break;


        default:
            break;
    }
}
